package cn.bobohost.market.common.util;


import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import javax.crypto.SecretKey;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

//加密工具

/**
 * JWT的加密的密码工具类
 */
public class JwtCryptographyUtil {

    /**
     * Key的加密算法，
     * 该算法名称来自于：io.jsonwebtoken.security.Keys中的常量
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 对称式加密的签名算法
     * 这里使用HS256，如果加密强度要求高，可以时候用HS512
     */
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM_HS = SignatureAlgorithm.HS256;

    /**
     * 非对称式加密的签名算法
     * 这里使用RS256，如果加密强度要求高，可以时候用RS512
     */
    private static final SignatureAlgorithm SIGNATURE_ALGORITHM_RS = SignatureAlgorithm.RS256;

    /**
     * 根据对称式加密的签名算法（HS）生成的随机密钥
     * @return
     * @throws Exception
     */
    public static SecretKey generateSecretKey()  {
        return Keys.secretKeyFor(SIGNATURE_ALGORITHM_HS);
    }

    /**
     * 根据非对称式加密的签名算法（RS）生成的随机密钥对（包含公钥和私钥）
     * @return
     * @throws Exception
     */
    public static KeyPair generatKeyPair()  {
        return Keys.keyPairFor(SIGNATURE_ALGORITHM_RS);
    }

    /**
     * 从字节中读取对称式加密签名的密钥
     * @param keyBytes
     * @return
     * @throws Exception
     */
    public static SecretKey getSecretKey(byte[] keyBytes) throws Exception {
//        keyBytes = Base64.getDecoder().decode(keyBytes);
//        System.out.println("==未解密=="+new String(keyBytes,"iso8859-1"));
//        SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
//        Mac mac = Mac.getInstance("HmacSHA256");
//        mac.init(signingKey);
//        keyBytes = mac.doFinal();
//        System.out.println("==解密后=="+new String(keyBytes,"iso8859-1"));
        return Keys.hmacShaKeyFor(keyBytes);
    }

    /**
     * 从字节中读取非对称式加密签名的公钥
     * @param keyBytes
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(byte[] keyBytes) throws Exception {
        //使用Base64解码一下，用于被Base64编码的key。没编码的不用解码。
//        keyBytes = Base64.getDecoder().decode(keyBytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return  keyFactory.generatePublic(spec);
    }

    /**
     * 从字节中读取非对称式加密签名的私钥
     * @param keyBytes
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(byte[] keyBytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
//        keyBytes = Base64.getDecoder().decode(keyBytes);
        //PKCS8EncodedKeySpec类使用PKCS#8标准作为密钥规范管理的编码格式
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
        return factory.generatePrivate(spec);
    }


    /**
     * 将字节写入文件中
     * @param filePathStr 文件路径字符串形式,如"/cattongue/ssh1/cattongue_hs_key"
     * @param bytes the byte array with the bytes to write
     * @throws IOException
     */
    public static void writeToFile(String filePathStr, byte[] bytes) throws IOException {
        //将字符串路径转成Path
        Path filePath = Path.of(filePathStr);
        //如果文件存在，根据当前日期备份一下
//        if(filePath.toFile().exists()){
//            Files.copy(filePath,filePath.resolveSibling(filePath.getFileName()));
//        }

        //获取文件的目录路径的文件对象
        File dirFile = filePath.getParent().toFile();

        //判断目录是否存在，如果不存在，则先创建目录
        if(!dirFile.exists()){
            //创建目录，包括子目录
            dirFile.mkdirs();
        }

        //将字节写入文件。若文件不存在，则自动创建；再次写入会覆盖原来的文件。
        Files.write(filePath,bytes);
    }


    /**
     *
     * @param filePathStr
     * @return
     * @throws Exception
     */
    public static byte[] readFromFile(String filePathStr) throws Exception {
        return Files.readAllBytes(Path.of(filePathStr));
    }


    /**
     * 写入随机生成的密钥到文件中
     * @param secretKeyFilePathStr
     * @throws Exception
     */
    public static void writeGeneratedSecretKeyToFile(String secretKeyFilePathStr) throws Exception{
        writeToFile(secretKeyFilePathStr,
                //获得字节码
                generateSecretKey().getEncoded());
    }

    /**
     * 写入随机生成的钥匙对（包含公钥和密钥）到文件中
     * @param privateKeyFilePathStr  这个是私钥的路径，公钥的文件路径加上后缀.pub。如私钥路径为"/cattongue/ssh/cattongue_rs_key"，则公钥路径为"/cattongue/ssh/cattongue_rs_key.pub"
     * @throws Exception
     */
    public static void writeGeneratedKeyPairToFile(String privateKeyFilePathStr) throws Exception{

        KeyPair keyPair = generatKeyPair();
        //将私钥写入文件
        writeToFile(privateKeyFilePathStr,
                //获得字节码
                keyPair.getPrivate().getEncoded());
        //将公钥写入文件
        writeToFile(privateKeyFilePathStr+".pub",
                //获得字节码
                keyPair.getPublic().getEncoded());
    }


    /**
     * 从文件中读取密钥（对称式加密签名）对象
     * @param secretKeyFilePathStr
     * @return
     * @throws Exception
     */
    public static SecretKey readSecretKeyFromFile(String secretKeyFilePathStr) throws Exception{
        return getSecretKey(readFromFile(secretKeyFilePathStr));
    }

    /**
     * 从文件中读取私钥（非对称式加密签名）对象
     * @param privateKeyFilePathStr
     * @return
     * @throws Exception
     */
    public static PrivateKey readPrivateKeyFromFile(String privateKeyFilePathStr) throws Exception{
        return getPrivateKey(readFromFile(privateKeyFilePathStr));
    }


    /**
     * 从文件中读取公钥（非对称式加密签名）对象
     * @param publicKeyFilePathStr
     * @return
     * @throws Exception
     */
    public static PublicKey readPublicKeyFromFile(String publicKeyFilePathStr) throws Exception{
        return getPublicKey(readFromFile(publicKeyFilePathStr));
    }


    public static void main(String[] args) throws Exception {

        // ==============一.测试hs256算法密钥---写入文件版
        //-------1.生成密钥并写入文件
        //秘钥文件路径，若需要手动生成key，则需要修改路径
//        String secretKeyFilePathStr="/cattongue/ssh-test/cattongue_hs_key";
//
//
////        Path path = Path.of(secretKeyFilePathStr);
////
////        Files.copy(path,path.resolveSibling(path.getFileName().toString()+".bak"));
//
//        //生成hs算法的密钥并写入文件
//        writeGeneratedSecretKeyToFile(secretKeyFilePathStr);
//
//        //-------2.从文件读取密钥
//        //获取hs算法的密钥encryption cipher algorithm Cryptography
//        SecretKey secretKey = readSecretKeyFromFile(secretKeyFilePathStr);
//        System.out.println("---secretKey:" + secretKey);

        // ==============二.测试hs256算法密钥---写入文件版
        //-------1.生成密钥并写入文件
        //#rs算法私钥存放的文件路径
        String privateKeyFilePathStr= "/java/marketproject/ssh/cattongue_rs_key";
        //#rs算法公钥存放的文件路径
        String publicKeyFilePathStr= "/java/marketproject/ssh/cattongue_rs_key.pub";

        //生成hs算法的密钥并写入文件
        writeGeneratedKeyPairToFile(privateKeyFilePathStr);


        //-------2.从文件读取密钥
        PrivateKey privateKey = readPrivateKeyFromFile(privateKeyFilePathStr);
        PublicKey publicKey = readPublicKeyFromFile(publicKeyFilePathStr);
        System.out.println("---privateKey:" + privateKey);
        System.out.println("---publicKey:" + publicKey);

        // ===========================================================================================
/*

        // ==============三..测试hs256算法密钥---写入其他设备版
        //-------1.生成密钥
        // 生成hs算法的密钥
        SecretKey secretKey1 = generateSecretKey();
        byte[] secretKeyBytes = secretKey1.getEncoded();
        // 提示，若写入其他设备时可用Base64编码一下，提高兼容性。该动作可选。
        secretKeyBytes=Base64.getEncoder().encode(secretKeyBytes);
        System.out.println("=====生成的对称式加密签名的密钥（可写入数据库或文件中）："+new String(secretKeyBytes, "iso8859-1"));


        //-------2.从其他设备读取密钥后解析
        //先base64解码
        secretKeyBytes = Base64.getDecoder().decode(secretKeyBytes);
        //根据字节获取密钥
        SecretKey secretKey2 = getSecretKey(secretKeyBytes);
        System.out.println("===文件读取还原回来的密钥"+new String(secretKey2.getEncoded(), "iso8859-1"));


        // ==============四..测试rs256算法密钥---写入其他设备版
        //生成
        KeyPair keyPair = generatKeyPair();
        PublicKey publicKey1 = keyPair.getPublic();
        PrivateKey privateKey1 = keyPair.getPrivate();

        //获取密钥字节
        byte[] privateKeyBytes = privateKey1.getEncoded();
        byte[] publicKeyBytes = publicKey1.getEncoded();

        // 提示，若写入其他设备时可用Base64编码一下，提高兼容性。该动作可选。
        privateKeyBytes=Base64.getEncoder().encode(privateKeyBytes);
        publicKeyBytes=Base64.getEncoder().encode(publicKeyBytes);

        System.out.println("====生成的非对称式加密签名的私钥（可写入数据库或文件中）:"+new String(privateKeyBytes,"iso8859-1"));
        System.out.println("====生成的非对称式加密签名的公钥（可写入数据库或文件中）:"+new String(publicKeyBytes,"iso8859-1"));

        //-------2.从其他设备读取密钥后解析
        //先base64解码
        privateKeyBytes = Base64.getDecoder().decode(privateKeyBytes);
        publicKeyBytes = Base64.getDecoder().decode(publicKeyBytes);

        //还原出公钥和私钥
        PrivateKey privateKey2 = getPrivateKey(privateKeyBytes);
        PublicKey publicKey2 = getPublicKey(publicKeyBytes);

        System.out.println("===文件读取还原回来的私钥:"+new String(privateKey2.getEncoded(),"iso8859-1"));
        System.out.println("===文件读取还原回来的公钥:"+new String(publicKey2.getEncoded(),"iso8859-1"));

*/

    }

}
